package db

import (
	"encoding/json"
	"errors"
	"fmt"
	"hr/api/dto"
	"strings"

	treeUtil "github.com/bing127/go_utils"
	"github.com/spf13/cast"
	"gorm.io/gorm"
)

type User struct {
	AppModel
	Username string `gorm:"size:64" json:"username"`
	Name     string `gorm:"size:64" json:"name"`
	Stature  string `gorm:"size:64" json:"stature"`
	Nation   string `gorm:"size:64" json:"nation"`
	Marriage string `gorm:"size:64" json:"marriage"`
	Address  string `gorm:"size:64" json:"address"`

	Password     string `gorm:"size:128" json:"password"`
	NickName     string `gorm:"size:128" json:"nickName"`      // 昵称
	Phone        string `gorm:"size:11" json:"phone"`          // 手机号
	Avatar       string `gorm:"size:255" json:"avatar"`        //头像
	Sex          string `gorm:"size:255" json:"sex"`           //性别
	Email        string `gorm:"size:128" json:"email"`         //邮箱
	CreateBy     string `gorm:"size:128" json:"createBy"`      // 创建人
	UpdateBy     string `gorm:"size:128" json:"updateBy"`      // 更新人
	OpenId       string `gorm:"size:255" json:"open_id"`       // openid
	WechatName   string `gorm:"size:255" json:"wechat_name"`   //微信昵称
	WechatAvatar string `gorm:"size:255" json:"wechat_avatar"` //微信头像
	Status       string `gorm:"size:4;" json:"status"`         // 用户状态  1禁用 0启用 默认启用
	No           string `gorm:"size:64" json:"no"`
	IDCard       string `gorm:"size:64" json:"id_card"`
	Age          string `gorm:"size:64" json:"age"`
	Education    string `gorm:"size:255" json:"education"`
	NativePlace  string `gorm:"size:255" json:"native_place"`
	DeptID       uint   `gorm:"size:64" json:"dept_id"`
	PostID       uint   `gorm:"size:64" json:"post_id"`
	EntryTime    string `gorm:"size:64" json:"entry_time"`
	FormalTime   string `gorm:"size:64" json:"formal_time"`
	LeaveTime    string `gorm:"size:64" json:"leave_time"`
	Money        string `gorm:"size:64" json:"money"`
	Contract     string `gorm:"size:64" json:"contract"`
	Birthday     string `gorm:"size:255" json:"birthday"`
	IsStaff      string `gorm:"size:64" json:"is_staff"`

	RoleID string `gorm:"size:255;NULL" json:"role_id"`
	Remark string `gorm:"size:255" json:"remark"` //备注
}

func (u *User) TableName() string {
	return "user"
}

type UserAndMenu struct {
	User     User                     `json:"user"`
	Role     []Role                   `json:"role"`
	Menu     []Menu                   `json:"-"`
	MenuTree []map[string]interface{} `json:"menu"`
}

type UserAndRole struct {
	User
	RoleName string `json:"role_name"`
	DeptName string `json:"dept_name"`
	PostName string `json:"post_name"`
}

type UserPageModel struct {
	Total    int64         `json:"total"`
	Result   []UserAndRole `json:"result"`
	PageNo   int           `json:"pageNo"`
	PageSize int           `json:"pageSize"`
}

func GetUserPaging(pagination dto.Pagination, name, phone, openid string) (UserPageModel, error) {
	result := UserPageModel{
		Total:    0,
		Result:   make([]UserAndRole, 0),
		PageNo:   pagination.PageNo,
		PageSize: pagination.PageSize,
	}
	offset := (pagination.PageNo - 1) * pagination.PageSize
	dbs := GetInstance().Select("user.*,GROUP_CONCAT(role.NAME) AS role_name, dept.name as dept_name, post.name as post_name").Joins("left join role on FIND_IN_SET( role.id, `user`.role_id )").Joins("left join dept on dept.id = user.dept_id").Joins("left join post on post.id = user.post_id").Group("user.id").Model(&User{})
	if name != "" {
		dbs = dbs.Where("user.username LIKE ?", "%"+name+"%")
	}
	if phone != "" {
		dbs = dbs.Where("user.phone LIKE ?", "%"+phone+"%")
	}

	if openid != "" {
		dbs = dbs.Where("user.open_id != ''")
	}

	if err := dbs.Offset(offset).Limit(pagination.PageSize).Find(&result.Result).Error; err != nil {
		return result, err
	}
	if err := dbs.Offset(-1).Limit(-1).Count(&result.Total).Error; err != nil {
		return result, err
	}
	return result, nil
}

func GetUserByDeptIdPaging(pagination dto.Pagination, deptId string) (UserPageModel, error) {
	result := UserPageModel{
		Total:    0,
		Result:   make([]UserAndRole, 0),
		PageNo:   pagination.PageNo,
		PageSize: pagination.PageSize,
	}
	offset := (pagination.PageNo - 1) * pagination.PageSize
	dbs := GetInstance().Select("user.*,GROUP_CONCAT(role.NAME) AS role_name, post.name as post_name, dict_value.tag as education_name").Joins("left join role on FIND_IN_SET( role.id, `user`.role_id )").Joins("left join post on post.id = user.post_id").Joins("left join dict_value on dict_value.id = user.education").Group("user.id").Model(&User{}).Where("user.dept_id = ? OR user.dept_id IS NULL", deptId)

	if err := dbs.Offset(offset).Limit(pagination.PageSize).Find(&result.Result).Error; err != nil {
		return result, err
	}
	if err := dbs.Offset(-1).Limit(-1).Count(&result.Total).Error; err != nil {
		return result, err
	}
	return result, nil
}

func GetUserByDeptPaging(pagination dto.Pagination, deptId string) (UserPageModel, error) {
	result := UserPageModel{
		Total:    0,
		Result:   make([]UserAndRole, 0),
		PageNo:   pagination.PageNo,
		PageSize: pagination.PageSize,
	}

	dept := Dept{}
	power := 2
	GetInstance().Where("id=?", deptId).First(&dept)
	if dept.ParentId == 0 {
		power = 1
	} else {
		GetInstance().Where("id=?", dept.ParentId).First(&dept)
		if dept.ParentId != 0 {
			deptId = cast.ToString(dept.ID)
		}
	}

	offset := (pagination.PageNo - 1) * pagination.PageSize
	dbs := GetInstance().Select("user.*,GROUP_CONCAT(role.NAME) AS role_name, post.name as post_name, dict_value.tag as education_name").Joins("left join role on FIND_IN_SET( role.id, `user`.role_id )").Joins("left join post on post.id = user.post_id").Joins("left join dict_value on dict_value.id = user.education")

	if power == 2 {
		dbs = dbs.Joins("left join dept on user.dept_id = dept.id").Group("user.id").Model(&User{}).Where("user.dept_id = ? or dept.parent_id=?", deptId, deptId)
	} else {
		dbs = dbs.Group("user.id").Model(&User{})
	}

	if err := dbs.Offset(offset).Limit(pagination.PageSize).Find(&result.Result).Error; err != nil {
		return result, err
	}
	if err := dbs.Offset(-1).Limit(-1).Count(&result.Total).Error; err != nil {
		return result, err
	}
	return result, nil
}

// GetUsers 根据条件查找
func GetUsers(p map[string]interface{}) (User, error, string) {
	f := User{}
	err := GetInstance().Where(p).First(&f).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return f, errors.New("查无记录"), "0"
		}
		return f, errors.New(err.Error()), "1"
	}
	return f, nil, "2"
}

// RegisterUser 注册用户
func RegisterUser(p dto.RegisterRequest) (User, error) {
	f := User{
		Username: p.Username,
		Password: p.Password,
		Status:   "1",
		Email:    p.Email,
	}
	if err := GetInstance().Create(&f).Error; err != nil {
		return f, errors.New("创建用户失败")
	}
	return f, nil
}

// UpdatePassword 修改密码
func UpdatePassword(id uint, pwd string) (User, error) {
	f := User{
		AppModel: AppModel{
			ID: id,
		},
	}
	err := GetInstance().Model(&f).Updates(User{
		Password: pwd,
	}).Scan(&f).Error
	if err != nil {
		return f, err
	}
	return f, nil
}

// GetUserRole 根据用户id查找角色
func GetUserRole(id uint) ([]Role, error) {
	r := make([]Role, 0)
	user, err, _ := GetUsers(map[string]interface{}{
		"id": id,
	})
	if err != nil {
		return nil, err
	}

	roleList := strings.Split(user.RoleID, ",")
	err = GetInstance().Where("id IN ?", roleList).Find(&r).Error
	if err != nil {
		return r, err
	}
	return r, nil
}

func GetRoleList(roleId string) ([]Role, error) {
	r := make([]Role, 0)
	roleList := strings.Split(roleId, ",")
	err := GetInstance().Where("id IN ?", roleList).Find(&r).Error
	if err != nil {
		return r, err
	}
	return r, nil
}

// GetUserMenu 获取用户id查找菜单
func GetUserMenu(id uint) ([]Menu, error) {
	m := make([]Menu, 0)
	menuId := make([]uint, 0)
	user := User{}
	if err := GetInstance().Where("id = ?", id).First(&user).Error; err != nil {
		return m, err
	}
	if user.RoleID == "" {
		return m, nil
	}
	if err := GetInstance().Raw(fmt.Sprintf("SELECT role_menu.menu_id FROM role_menu WHERE role_menu.role_id IN (%s)", user.RoleID)).Scan(&menuId).Error; err != nil {
		return m, err
	}
	if err := GetInstance().Model(&Menu{}).Where("menu.id IN ?", menuId).Find(&m).Error; err != nil {
		return m, err
	}
	return m, nil
}

func GetUserInfo(id uint) (UserAndMenu, error) {
	info := UserAndMenu{}
	u, err, tag := GetUsers(map[string]interface{}{
		"id": id,
	})

	if tag != "2" {
		return info, err
	}
	info.User = u

	r, err := GetRoleList(u.RoleID)
	if err != nil {
		return info, err
	}
	info.Role = r

	m, err := GetUserMenu(id)
	if err != nil {
		return info, err
	}
	info.Menu = m

	config := treeUtil.TreeConfig{
		ID:       "id",
		PID:      "parent_id",
		Children: "children",
	}
	mMap := make([]map[string]interface{}, 0)
	if len(m) > 0 {
		marshal, _ := json.Marshal(m)
		_ = json.Unmarshal(marshal, &mMap)
		i := config.GenerateTree(mMap)
		info.MenuTree = i
	}
	return info, nil
}

func CreateUser(p dto.UserRequest, user string) (User, error) {
	d := User{
		Username:     p.Username,
		IsStaff:      p.IsStaff,
		Password:     p.Password,
		NickName:     p.NickName,
		Name:         p.Name,
		Stature:      p.Stature,
		Nation:       p.Nation,
		Marriage:     p.Marriage,
		Address:      p.Address,
		Phone:        p.Phone,
		Avatar:       p.Avatar,
		Sex:          cast.ToString(p.Sex),
		Email:        p.Email,
		CreateBy:     user,
		OpenId:       p.OpenId,
		WechatName:   p.WechatName,
		WechatAvatar: p.WechatAvatar,
		Remark:       p.Remark,
		Status:       p.Status,
		Education:    cast.ToString(p.Education),
		Birthday:     p.Birthday,
		RoleID:       p.RoleID,
		No:           p.No,
		IDCard:       p.IDCard,
		Age:          cast.ToString(p.Age),
		NativePlace:  p.NativePlace,
		DeptID:       cast.ToUint(p.DeptID),
		PostID:       cast.ToUint(p.PostID),
		EntryTime:    p.EntryTime,
		FormalTime:   p.FormalTime,
		LeaveTime:    p.LeaveTime,
		Money:        cast.ToString(p.Money),
		Contract:     p.Contract,
	}
	if err := GetInstance().Omit("ClassesID").Create(&d).Error; err != nil {
		return d, err
	}
	return d, nil
}

func UpdateUser(id uint, p dto.UserRequest, userId string) (User, error) {
	f := User{
		AppModel: AppModel{
			ID: id,
		},
	}
	var roleId interface{}
	if p.RoleID == "" {
		roleId = nil
	} else {
		roleId = p.RoleID
	}
	isStaff := ""
	if p.RoleID != "" {
		isStaff = "4"
	}

	err := GetInstance().Model(&f).Updates(User{
		Username:     p.Username,
		Name:         p.Name,
		Stature:      p.Stature,
		Nation:       p.Nation,
		Marriage:     p.Marriage,
		Address:      p.Address,
		Password:     p.Password,
		IsStaff:      isStaff,
		NickName:     p.NickName,
		Phone:        p.Phone,
		Avatar:       p.Avatar,
		Sex:          cast.ToString(p.Sex),
		Email:        p.Email,
		UpdateBy:     userId,
		OpenId:       p.OpenId,
		WechatName:   p.WechatName,
		WechatAvatar: p.WechatAvatar,
		Remark:       p.Remark,
		Status:       p.Status,
		Education:    cast.ToString(p.Education),
		Birthday:     p.Birthday,
		RoleID:       p.RoleID,
		No:           p.No,
		IDCard:       p.IDCard,
		Age:          cast.ToString(p.Age),
		NativePlace:  p.NativePlace,
		DeptID:       cast.ToUint(p.DeptID),
		PostID:       cast.ToUint(p.PostID),
		EntryTime:    p.EntryTime,
		FormalTime:   p.FormalTime,
		LeaveTime:    p.LeaveTime,
		Money:        cast.ToString(p.Money),
		Contract:     p.Contract,
	}).Updates(map[string]interface{}{
		"role_id": roleId,
	}).Scan(&f).Error
	if err != nil {
		return f, err
	}
	return f, nil
}

func DeleteUser(id uint) (User, error) {
	f := User{}
	err := GetInstance().Unscoped().Where("id = ?", id).Delete(&f).Error
	if err != nil {
		return f, err
	}
	return f, nil
}

func GetUserById(id uint) User {
	user := User{}
	GetInstance().Where("id=?", id).First(&user)
	return user
}
